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