45d37436a325dec9840a38644e2fba606f3d4208
[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 lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1638     int lypos = MICROLABEL_YPOS;
1639
1640     DrawText(lxpos, lypos, label_text, font_nr);
1641   }
1642
1643   redraw_mask |= REDRAW_MICROLEVEL;
1644 }
1645
1646 void DrawMicroLevel(int xpos, int ypos, boolean restart)
1647 {
1648   static unsigned long scroll_delay = 0;
1649   static unsigned long label_delay = 0;
1650   static int from_x, from_y, scroll_direction;
1651   static int label_state, label_counter;
1652   int last_game_status = game_status;   /* save current game status */
1653
1654   /* force PREVIEW font on preview level */
1655   game_status = GAME_MODE_PSEUDO_PREVIEW;
1656
1657   if (restart)
1658   {
1659     from_x = from_y = 0;
1660     scroll_direction = MV_RIGHT;
1661     label_state = 1;
1662     label_counter = 0;
1663
1664     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1665     DrawMicroLevelLabelExt(label_state);
1666
1667     /* initialize delay counters */
1668     DelayReached(&scroll_delay, 0);
1669     DelayReached(&label_delay, 0);
1670
1671     if (leveldir_current->name)
1672     {
1673       int text_width = getTextWidth(leveldir_current->name, FONT_TEXT_1);
1674       int lxpos = SX + (SXSIZE - text_width) / 2;
1675       int lypos = SY + 352;
1676
1677       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
1678     }
1679
1680     game_status = last_game_status;     /* restore current game status */
1681
1682     return;
1683   }
1684
1685   /* scroll micro level, if needed */
1686   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1687       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1688   {
1689     switch (scroll_direction)
1690     {
1691       case MV_LEFT:
1692         if (from_x > 0)
1693           from_x--;
1694         else
1695           scroll_direction = MV_UP;
1696         break;
1697
1698       case MV_RIGHT:
1699         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1700           from_x++;
1701         else
1702           scroll_direction = MV_DOWN;
1703         break;
1704
1705       case MV_UP:
1706         if (from_y > 0)
1707           from_y--;
1708         else
1709           scroll_direction = MV_RIGHT;
1710         break;
1711
1712       case MV_DOWN:
1713         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1714           from_y++;
1715         else
1716           scroll_direction = MV_LEFT;
1717         break;
1718
1719       default:
1720         break;
1721     }
1722
1723     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1724   }
1725
1726   /* redraw micro level label, if needed */
1727   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
1728       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
1729       strcmp(level.author, leveldir_current->name) != 0 &&
1730       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1731   {
1732     int max_label_counter = 23;
1733
1734     if (leveldir_current->imported_from != NULL)
1735       max_label_counter += 14;
1736
1737     label_counter = (label_counter + 1) % max_label_counter;
1738     label_state = (label_counter >= 0 && label_counter <= 7 ?
1739                    MICROLABEL_LEVEL_NAME :
1740                    label_counter >= 9 && label_counter <= 12 ?
1741                    MICROLABEL_CREATED_BY :
1742                    label_counter >= 14 && label_counter <= 21 ?
1743                    MICROLABEL_LEVEL_AUTHOR :
1744                    label_counter >= 23 && label_counter <= 26 ?
1745                    MICROLABEL_IMPORTED_FROM :
1746                    label_counter >= 28 && label_counter <= 35 ?
1747                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
1748     DrawMicroLevelLabelExt(label_state);
1749   }
1750
1751   game_status = last_game_status;       /* restore current game status */
1752 }
1753
1754 int REQ_in_range(int x, int y)
1755 {
1756   if (y > DY+249 && y < DY+278)
1757   {
1758     if (x > DX+1 && x < DX+48)
1759       return 1;
1760     else if (x > DX+51 && x < DX+98) 
1761       return 2;
1762   }
1763   return 0;
1764 }
1765
1766 #define MAX_REQUEST_LINES               13
1767 #define MAX_REQUEST_LINE_LEN            7
1768
1769 boolean Request(char *text, unsigned int req_state)
1770 {
1771   int mx, my, ty, result = -1;
1772   unsigned int old_door_state;
1773   int last_game_status = game_status;   /* save current game status */
1774
1775 #if defined(PLATFORM_UNIX)
1776   /* pause network game while waiting for request to answer */
1777   if (options.network &&
1778       game_status == GAME_MODE_PLAYING &&
1779       req_state & REQUEST_WAIT_FOR)
1780     SendToServer_PausePlaying();
1781 #endif
1782
1783   old_door_state = GetDoorState();
1784
1785   /* simulate releasing mouse button over last gadget, if still pressed */
1786   if (button_status)
1787     HandleGadgets(-1, -1, 0);
1788
1789   UnmapAllGadgets();
1790
1791   CloseDoor(DOOR_CLOSE_1);
1792
1793   /* save old door content */
1794   BlitBitmap(bitmap_db_door, bitmap_db_door,
1795              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
1796              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
1797
1798   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1799
1800   /* clear door drawing field */
1801   DrawBackground(DX, DY, DXSIZE, DYSIZE);
1802
1803   /* force DOOR font on preview level */
1804   game_status = GAME_MODE_PSEUDO_DOOR;
1805
1806   /* write text for request */
1807   for(ty=0; ty < MAX_REQUEST_LINES; ty++)
1808   {
1809     char text_line[MAX_REQUEST_LINE_LEN + 1];
1810     int tx, tl, tc;
1811
1812     if (!*text)
1813       break;
1814
1815     for(tl=0,tx=0; tx < MAX_REQUEST_LINE_LEN; tl++,tx++)
1816     {
1817       tc = *(text + tx);
1818       if (!tc || tc == ' ')
1819         break;
1820     }
1821
1822     if (!tl)
1823     { 
1824       text++; 
1825       ty--; 
1826       continue; 
1827     }
1828
1829     strncpy(text_line, text, tl);
1830     text_line[tl] = 0;
1831
1832     DrawText(DX + (DXSIZE - tl * getFontWidth(FONT_TEXT_2)) / 2,
1833              DY + 8 + ty * (getFontHeight(FONT_TEXT_2) + 2),
1834              text_line, FONT_TEXT_2);
1835
1836     text += tl + (tc == ' ' ? 1 : 0);
1837   }
1838
1839   game_status = last_game_status;       /* restore current game status */
1840
1841   if (req_state & REQ_ASK)
1842   {
1843     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
1844     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
1845   }
1846   else if (req_state & REQ_CONFIRM)
1847   {
1848     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
1849   }
1850   else if (req_state & REQ_PLAYER)
1851   {
1852     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
1853     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
1854     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
1855     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
1856   }
1857
1858   /* copy request gadgets to door backbuffer */
1859   BlitBitmap(drawto, bitmap_db_door,
1860              DX, DY, DXSIZE, DYSIZE,
1861              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1862
1863   OpenDoor(DOOR_OPEN_1);
1864
1865 #if 0
1866   ClearEventQueue();
1867 #endif
1868
1869   if (!(req_state & REQUEST_WAIT_FOR))
1870   {
1871     SetDrawBackgroundMask(REDRAW_FIELD);
1872
1873     return FALSE;
1874   }
1875
1876   if (game_status != GAME_MODE_MAIN)
1877     InitAnimation();
1878
1879   button_status = MB_RELEASED;
1880
1881   request_gadget_id = -1;
1882
1883   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1884
1885   SetMouseCursor(CURSOR_DEFAULT);
1886
1887   while(result < 0)
1888   {
1889     if (PendingEvent())
1890     {
1891       Event event;
1892
1893       NextEvent(&event);
1894
1895       switch(event.type)
1896       {
1897         case EVENT_BUTTONPRESS:
1898         case EVENT_BUTTONRELEASE:
1899         case EVENT_MOTIONNOTIFY:
1900         {
1901           if (event.type == EVENT_MOTIONNOTIFY)
1902           {
1903             if (!PointerInWindow(window))
1904               continue; /* window and pointer are on different screens */
1905
1906             if (!button_status)
1907               continue;
1908
1909             motion_status = TRUE;
1910             mx = ((MotionEvent *) &event)->x;
1911             my = ((MotionEvent *) &event)->y;
1912           }
1913           else
1914           {
1915             motion_status = FALSE;
1916             mx = ((ButtonEvent *) &event)->x;
1917             my = ((ButtonEvent *) &event)->y;
1918             if (event.type == EVENT_BUTTONPRESS)
1919               button_status = ((ButtonEvent *) &event)->button;
1920             else
1921               button_status = MB_RELEASED;
1922           }
1923
1924           /* this sets 'request_gadget_id' */
1925           HandleGadgets(mx, my, button_status);
1926
1927           switch(request_gadget_id)
1928           {
1929             case TOOL_CTRL_ID_YES:
1930               result = TRUE;
1931               break;
1932             case TOOL_CTRL_ID_NO:
1933               result = FALSE;
1934               break;
1935             case TOOL_CTRL_ID_CONFIRM:
1936               result = TRUE | FALSE;
1937               break;
1938
1939             case TOOL_CTRL_ID_PLAYER_1:
1940               result = 1;
1941               break;
1942             case TOOL_CTRL_ID_PLAYER_2:
1943               result = 2;
1944               break;
1945             case TOOL_CTRL_ID_PLAYER_3:
1946               result = 3;
1947               break;
1948             case TOOL_CTRL_ID_PLAYER_4:
1949               result = 4;
1950               break;
1951
1952             default:
1953               break;
1954           }
1955
1956           break;
1957         }
1958
1959         case EVENT_KEYPRESS:
1960           switch(GetEventKey((KeyEvent *)&event, TRUE))
1961           {
1962             case KSYM_Return:
1963               result = 1;
1964               break;
1965
1966             case KSYM_Escape:
1967               result = 0;
1968               break;
1969
1970             default:
1971               break;
1972           }
1973           if (req_state & REQ_PLAYER)
1974             result = 0;
1975           break;
1976
1977         case EVENT_KEYRELEASE:
1978           ClearPlayerAction();
1979           break;
1980
1981         default:
1982           HandleOtherEvents(&event);
1983           break;
1984       }
1985     }
1986     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
1987     {
1988       int joy = AnyJoystick();
1989
1990       if (joy & JOY_BUTTON_1)
1991         result = 1;
1992       else if (joy & JOY_BUTTON_2)
1993         result = 0;
1994     }
1995
1996     DoAnimation();
1997
1998     /* don't eat all CPU time */
1999     Delay(10);
2000   }
2001
2002   if (game_status != GAME_MODE_MAIN)
2003     StopAnimation();
2004
2005   UnmapToolButtons();
2006
2007   if (!(req_state & REQ_STAY_OPEN))
2008   {
2009     CloseDoor(DOOR_CLOSE_1);
2010
2011     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
2012     {
2013       BlitBitmap(bitmap_db_door, bitmap_db_door,
2014                  DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
2015                  DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
2016       OpenDoor(DOOR_OPEN_1);
2017     }
2018   }
2019
2020   RemapAllGadgets();
2021
2022   SetDrawBackgroundMask(REDRAW_FIELD);
2023
2024 #if defined(PLATFORM_UNIX)
2025   /* continue network game after request */
2026   if (options.network &&
2027       game_status == GAME_MODE_PLAYING &&
2028       req_state & REQUEST_WAIT_FOR)
2029     SendToServer_ContinuePlaying();
2030 #endif
2031
2032   return result;
2033 }
2034
2035 unsigned int OpenDoor(unsigned int door_state)
2036 {
2037   unsigned int new_door_state;
2038
2039   if (door_state & DOOR_COPY_BACK)
2040   {
2041     BlitBitmap(bitmap_db_door, bitmap_db_door,
2042                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
2043                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2044     door_state &= ~DOOR_COPY_BACK;
2045   }
2046
2047   new_door_state = MoveDoor(door_state);
2048
2049   return(new_door_state);
2050 }
2051
2052 unsigned int CloseDoor(unsigned int door_state)
2053 {
2054   unsigned int new_door_state;
2055
2056   BlitBitmap(backbuffer, bitmap_db_door,
2057              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2058   BlitBitmap(backbuffer, bitmap_db_door,
2059              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2060
2061   new_door_state = MoveDoor(door_state);
2062
2063   return(new_door_state);
2064 }
2065
2066 unsigned int GetDoorState()
2067 {
2068   return MoveDoor(DOOR_GET_STATE);
2069 }
2070
2071 unsigned int SetDoorState(unsigned int door_state)
2072 {
2073   return MoveDoor(door_state | DOOR_SET_STATE);
2074 }
2075
2076 unsigned int MoveDoor(unsigned int door_state)
2077 {
2078   static int door1 = DOOR_OPEN_1;
2079   static int door2 = DOOR_CLOSE_2;
2080   static unsigned long door_delay = 0;
2081   int x, start, stepsize = door.step_offset;
2082   unsigned long door_delay_value = door.step_delay;
2083
2084   if (door_state == DOOR_GET_STATE)
2085     return(door1 | door2);
2086
2087   if (door_state & DOOR_SET_STATE)
2088   {
2089     if (door_state & DOOR_ACTION_1)
2090       door1 = door_state & DOOR_ACTION_1;
2091     if (door_state & DOOR_ACTION_2)
2092       door2 = door_state & DOOR_ACTION_2;
2093
2094     return(door1 | door2);
2095   }
2096
2097   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2098     door_state &= ~DOOR_OPEN_1;
2099   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2100     door_state &= ~DOOR_CLOSE_1;
2101   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2102     door_state &= ~DOOR_OPEN_2;
2103   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2104     door_state &= ~DOOR_CLOSE_2;
2105
2106   if (setup.quick_doors)
2107   {
2108     stepsize = 20;
2109     door_delay_value = 0;
2110
2111     StopSound(SND_DOOR_OPENING);
2112     StopSound(SND_DOOR_CLOSING);
2113   }
2114
2115   if (global.autoplay_leveldir)
2116   {
2117     door_state |= DOOR_NO_DELAY;
2118     door_state &= ~DOOR_CLOSE_ALL;
2119   }
2120
2121   if (door_state & DOOR_ACTION)
2122   {
2123     if (!(door_state & DOOR_NO_DELAY))
2124     {
2125       /* opening door sound has priority over simultaneously closing door */
2126       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2127         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2128       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2129         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2130     }
2131
2132     start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2133
2134     for(x=start; x<=DXSIZE; x+=stepsize)
2135     {
2136       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2137       GC gc = bitmap->stored_clip_gc;
2138
2139       if (!(door_state & DOOR_NO_DELAY))
2140         WaitUntilDelayReached(&door_delay, door_delay_value);
2141
2142       if (door_state & DOOR_ACTION_1)
2143       {
2144         int i = (door_state & DOOR_OPEN_1 ? DXSIZE-x : x);
2145         int j = (DXSIZE - i) / 3;
2146
2147         BlitBitmap(bitmap_db_door, drawto,
2148                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i/2,
2149                    DXSIZE,DYSIZE - i/2, DX, DY);
2150
2151         ClearRectangle(drawto, DX, DY + DYSIZE - i/2, DXSIZE,i/2);
2152
2153         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2154         BlitBitmapMasked(bitmap, drawto,
2155                          DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2156                          DX + DXSIZE - i, DY + j);
2157         BlitBitmapMasked(bitmap, drawto,
2158                          DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2159                          DX + DXSIZE - i, DY + 140 + j);
2160         SetClipOrigin(bitmap, gc, DX - DXSIZE + i, DY - (DOOR_GFX_PAGEY1 + j));
2161         BlitBitmapMasked(bitmap, drawto,
2162                          DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2163                          DX, DY);
2164         BlitBitmapMasked(bitmap, drawto,
2165                          DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2166                          DX, DY + 140 - j);
2167
2168         BlitBitmapMasked(bitmap, drawto,
2169                          DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2170                          DX, DY + 77 - j);
2171         BlitBitmapMasked(bitmap, drawto,
2172                          DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2173                          DX, DY + 203 - j);
2174         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2175         BlitBitmapMasked(bitmap, drawto,
2176                          DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2177                          DX + DXSIZE - i, DY + 77 + j);
2178         BlitBitmapMasked(bitmap, drawto,
2179                          DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2180                          DX + DXSIZE - i, DY + 203 + j);
2181
2182         redraw_mask |= REDRAW_DOOR_1;
2183       }
2184
2185       if (door_state & DOOR_ACTION_2)
2186       {
2187         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - x : x);
2188         int j = (VXSIZE - i) / 3;
2189
2190         BlitBitmap(bitmap_db_door, drawto,
2191                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i/2,
2192                    VXSIZE, VYSIZE - i/2, VX, VY);
2193
2194         ClearRectangle(drawto, VX, VY + VYSIZE-i/2, VXSIZE, i/2);
2195
2196         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2197         BlitBitmapMasked(bitmap, drawto,
2198                          VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2199                          VX + VXSIZE-i, VY+j);
2200         SetClipOrigin(bitmap, gc,
2201                       VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2202         BlitBitmapMasked(bitmap, drawto,
2203                          VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2204                          VX, VY);
2205
2206         BlitBitmapMasked(bitmap, drawto,
2207                          VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2208                          i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2209         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2210         BlitBitmapMasked(bitmap, drawto,
2211                          VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2212                          i, VYSIZE / 2 - j,
2213                          VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2214
2215         redraw_mask |= REDRAW_DOOR_2;
2216       }
2217
2218       BackToFront();
2219
2220       if (game_status == GAME_MODE_MAIN)
2221         DoAnimation();
2222     }
2223   }
2224
2225   if (setup.quick_doors)
2226   {
2227     StopSound(SND_DOOR_OPENING);
2228     StopSound(SND_DOOR_CLOSING);
2229   }
2230
2231   if (door_state & DOOR_ACTION_1)
2232     door1 = door_state & DOOR_ACTION_1;
2233   if (door_state & DOOR_ACTION_2)
2234     door2 = door_state & DOOR_ACTION_2;
2235
2236   return (door1 | door2);
2237 }
2238
2239 void DrawSpecialEditorDoor()
2240 {
2241   /* draw bigger toolbox window */
2242   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2243              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2244              EX - 4, EY - 12);
2245   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2246              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2247              EX - 4, EY - 4);
2248
2249   redraw_mask |= REDRAW_ALL;
2250 }
2251
2252 void UndrawSpecialEditorDoor()
2253 {
2254   /* draw normal tape recorder window */
2255   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2256              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2257              EX - 4, EY - 12);
2258
2259   redraw_mask |= REDRAW_ALL;
2260 }
2261
2262
2263 /* ---------- new tool button stuff ---------------------------------------- */
2264
2265 /* graphic position values for tool buttons */
2266 #define TOOL_BUTTON_YES_XPOS            2
2267 #define TOOL_BUTTON_YES_YPOS            250
2268 #define TOOL_BUTTON_YES_GFX_YPOS        0
2269 #define TOOL_BUTTON_YES_XSIZE           46
2270 #define TOOL_BUTTON_YES_YSIZE           28
2271 #define TOOL_BUTTON_NO_XPOS             52
2272 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2273 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2274 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2275 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2276 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2277 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2278 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2279 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2280 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2281 #define TOOL_BUTTON_PLAYER_XSIZE        30
2282 #define TOOL_BUTTON_PLAYER_YSIZE        30
2283 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2284 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2285 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2286 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2287 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2288                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2289 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2290                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2291 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2292                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2293 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2294                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2295 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2296                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2297 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2298                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2299 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2300                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2301 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2302                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2303
2304 static struct
2305 {
2306   int xpos, ypos;
2307   int x, y;
2308   int width, height;
2309   int gadget_id;
2310   char *infotext;
2311 } toolbutton_info[NUM_TOOL_BUTTONS] =
2312 {
2313   {
2314     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2315     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2316     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2317     TOOL_CTRL_ID_YES,
2318     "yes"
2319   },
2320   {
2321     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2322     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2323     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2324     TOOL_CTRL_ID_NO,
2325     "no"
2326   },
2327   {
2328     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2329     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2330     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2331     TOOL_CTRL_ID_CONFIRM,
2332     "confirm"
2333   },
2334   {
2335     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2336     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2337     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2338     TOOL_CTRL_ID_PLAYER_1,
2339     "player 1"
2340   },
2341   {
2342     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2343     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2344     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2345     TOOL_CTRL_ID_PLAYER_2,
2346     "player 2"
2347   },
2348   {
2349     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2350     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2351     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2352     TOOL_CTRL_ID_PLAYER_3,
2353     "player 3"
2354   },
2355   {
2356     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2357     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2358     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2359     TOOL_CTRL_ID_PLAYER_4,
2360     "player 4"
2361   }
2362 };
2363
2364 void CreateToolButtons()
2365 {
2366   int i;
2367
2368   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2369   {
2370     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2371     Bitmap *deco_bitmap = None;
2372     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2373     struct GadgetInfo *gi;
2374     unsigned long event_mask;
2375     int gd_xoffset, gd_yoffset;
2376     int gd_x1, gd_x2, gd_y;
2377     int id = i;
2378
2379     event_mask = GD_EVENT_RELEASED;
2380
2381     gd_xoffset = toolbutton_info[i].xpos;
2382     gd_yoffset = toolbutton_info[i].ypos;
2383     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2384     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2385     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2386
2387     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2388     {
2389       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2390
2391       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2392                            &deco_bitmap, &deco_x, &deco_y);
2393       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2394       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2395     }
2396
2397     gi = CreateGadget(GDI_CUSTOM_ID, id,
2398                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2399                       GDI_X, DX + toolbutton_info[i].x,
2400                       GDI_Y, DY + toolbutton_info[i].y,
2401                       GDI_WIDTH, toolbutton_info[i].width,
2402                       GDI_HEIGHT, toolbutton_info[i].height,
2403                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2404                       GDI_STATE, GD_BUTTON_UNPRESSED,
2405                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2406                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2407                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2408                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2409                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2410                       GDI_DECORATION_SHIFTING, 1, 1,
2411                       GDI_EVENT_MASK, event_mask,
2412                       GDI_CALLBACK_ACTION, HandleToolButtons,
2413                       GDI_END);
2414
2415     if (gi == NULL)
2416       Error(ERR_EXIT, "cannot create gadget");
2417
2418     tool_gadget[id] = gi;
2419   }
2420 }
2421
2422 void FreeToolButtons()
2423 {
2424   int i;
2425
2426   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2427     FreeGadget(tool_gadget[i]);
2428 }
2429
2430 static void UnmapToolButtons()
2431 {
2432   int i;
2433
2434   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2435     UnmapGadget(tool_gadget[i]);
2436 }
2437
2438 static void HandleToolButtons(struct GadgetInfo *gi)
2439 {
2440   request_gadget_id = gi->custom_id;
2441 }
2442
2443 int get_next_element(int element)
2444 {
2445   switch(element)
2446   {
2447     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2448     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2449     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2450     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2451     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2452     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2453     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2454
2455     default:                            return element;
2456   }
2457 }
2458
2459 int el_act_dir2img(int element, int action, int direction)
2460 {
2461   element = GFX_ELEMENT(element);
2462   direction = MV_DIR_BIT(direction);
2463
2464   return element_info[element].direction_graphic[action][direction];
2465 }
2466
2467 static int el_act_dir2crm(int element, int action, int direction)
2468 {
2469   element = GFX_ELEMENT(element);
2470   direction = MV_DIR_BIT(direction);
2471
2472   return element_info[element].direction_crumbled[action][direction];
2473 }
2474
2475 int el_act2img(int element, int action)
2476 {
2477   element = GFX_ELEMENT(element);
2478
2479   return element_info[element].graphic[action];
2480 }
2481
2482 int el_dir2img(int element, int direction)
2483 {
2484   element = GFX_ELEMENT(element);
2485
2486   return el_act_dir2img(element, ACTION_DEFAULT, direction);
2487 }
2488
2489 int el2img(int element)
2490 {
2491   element = GFX_ELEMENT(element);
2492
2493   return element_info[element].graphic[ACTION_DEFAULT];
2494 }
2495
2496 int el2edimg(int element)
2497 {
2498   element = GFX_ELEMENT(element);
2499
2500   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
2501 }
2502
2503 int el2preimg(int element)
2504 {
2505   element = GFX_ELEMENT(element);
2506
2507   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
2508 }