rnd-20030726-1-src
[rocksndiamonds.git] / src / tools.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * tools.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "game.h"
18 #include "events.h"
19 #include "cartoons.h"
20 #include "network.h"
21 #include "tape.h"
22
23 /* tool button identifiers */
24 #define TOOL_CTRL_ID_YES        0
25 #define TOOL_CTRL_ID_NO         1
26 #define TOOL_CTRL_ID_CONFIRM    2
27 #define TOOL_CTRL_ID_PLAYER_1   3
28 #define TOOL_CTRL_ID_PLAYER_2   4
29 #define TOOL_CTRL_ID_PLAYER_3   5
30 #define TOOL_CTRL_ID_PLAYER_4   6
31
32 #define NUM_TOOL_BUTTONS        7
33
34 /* forward declaration for internal use */
35 static void UnmapToolButtons();
36 static void HandleToolButtons(struct GadgetInfo *);
37
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 ? GfxElement[x][y] : Feld[x][y]);
1204
1205   /* crumble field itself */
1206   if (CAN_BE_CRUMBLED(element) && !IS_MOVING(x, y))
1207   {
1208     if (!IN_SCR_FIELD(sx, sy))
1209       return;
1210
1211     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1212
1213     for(i=0; i<4; i++)
1214     {
1215       int xx = x + xy[i][0];
1216       int yy = y + xy[i][1];
1217
1218       element = (IN_LEV_FIELD(xx, yy) ? Feld[xx][yy] : BorderElement);
1219
1220       /* check if neighbour field is of same type */
1221       if (CAN_BE_CRUMBLED(element) && !IS_MOVING(xx, yy))
1222         continue;
1223
1224       if (i == 1 || i == 2)
1225       {
1226         width = snip;
1227         height = TILEY;
1228         cx = (i == 2 ? TILEX - snip : 0);
1229         cy = 0;
1230       }
1231       else
1232       {
1233         width = TILEX;
1234         height = snip;
1235         cx = 0;
1236         cy = (i == 3 ? TILEY - snip : 0);
1237       }
1238
1239       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1240                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
1241     }
1242
1243     MarkTileDirty(sx, sy);
1244   }
1245   else          /* crumble neighbour fields */
1246   {
1247     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1248
1249     for(i=0; i<4; i++)
1250     {
1251       int xx = x + xy[i][0];
1252       int yy = y + xy[i][1];
1253       int sxx = sx + xy[i][0];
1254       int syy = sy + xy[i][1];
1255
1256       if (!IN_LEV_FIELD(xx, yy) ||
1257           !IN_SCR_FIELD(sxx, syy) ||
1258           !CAN_BE_CRUMBLED(Feld[xx][yy]) ||
1259           IS_MOVING(xx, yy))
1260         continue;
1261
1262       if (i == 1 || i == 2)
1263       {
1264         width = snip;
1265         height = TILEY;
1266         cx = (i == 1 ? TILEX - snip : 0);
1267         cy = 0;
1268       }
1269       else
1270       {
1271         width = TILEX;
1272         height = snip;
1273         cx = 0;
1274         cy = (i==0 ? TILEY-snip : 0);
1275       }
1276
1277       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1278                  width, height, FX + sxx * TILEX + cx, FY + syy * TILEY + cy);
1279
1280       MarkTileDirty(sxx, syy);
1281     }
1282   }
1283 }
1284
1285 void DrawLevelFieldCrumbledSand(int x, int y)
1286 {
1287   DrawLevelFieldCrumbledSandExt(x, y, IMG_SAND_CRUMBLED, 0);
1288 }
1289
1290 void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
1291                                        int step_frame)
1292 {
1293   int graphic1 = el_act_dir2img(EL_SAND,          ACTION_DIGGING, direction);
1294   int graphic2 = el_act_dir2img(EL_SAND_CRUMBLED, ACTION_DIGGING, direction);
1295   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1296   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1297   int sx = SCREENX(x), sy = SCREENY(y);
1298
1299   DrawGraphic(sx, sy, graphic1, frame1);
1300   DrawLevelFieldCrumbledSandExt(x, y, graphic2, frame2);
1301 }
1302
1303 void DrawLevelFieldCrumbledSandNeighbours(int x, int y)
1304 {
1305   int sx = SCREENX(x), sy = SCREENY(y);
1306   static int xy[4][2] =
1307   {
1308     { 0, -1 },
1309     { -1, 0 },
1310     { +1, 0 },
1311     { 0, +1 }
1312   };
1313   int i;
1314
1315   for(i=0; i<4; i++)
1316   {
1317     int xx = x + xy[i][0];
1318     int yy = y + xy[i][1];
1319     int sxx = sx + xy[i][0];
1320     int syy = sy + xy[i][1];
1321
1322     if (!IN_LEV_FIELD(xx, yy) ||
1323         !IN_SCR_FIELD(sxx, syy) ||
1324         !CAN_BE_CRUMBLED(Feld[xx][yy]) ||
1325         IS_MOVING(xx, yy))
1326       continue;
1327
1328     DrawLevelField(xx, yy);
1329   }
1330 }
1331
1332 static int getBorderElement(int x, int y)
1333 {
1334   int border[7][2] =
1335   {
1336     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
1337     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
1338     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1339     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1340     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
1341     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
1342     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
1343   };
1344   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1345   int steel_position = (x == -1 && y == -1                      ? 0 :
1346                         x == lev_fieldx && y == -1              ? 1 :
1347                         x == -1 && y == lev_fieldy              ? 2 :
1348                         x == lev_fieldx && y == lev_fieldy      ? 3 :
1349                         x == -1 || x == lev_fieldx              ? 4 :
1350                         y == -1 || y == lev_fieldy              ? 5 : 6);
1351
1352   return border[steel_position][steel_type];
1353 }
1354
1355 void DrawScreenElement(int x, int y, int element)
1356 {
1357   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1358   DrawLevelFieldCrumbledSand(LEVELX(x), LEVELY(y));
1359 }
1360
1361 void DrawLevelElement(int x, int y, int element)
1362 {
1363   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1364     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1365 }
1366
1367 void DrawScreenField(int x, int y)
1368 {
1369   int lx = LEVELX(x), ly = LEVELY(y);
1370   int element, content;
1371
1372   if (!IN_LEV_FIELD(lx, ly))
1373   {
1374     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1375       element = EL_EMPTY;
1376     else
1377       element = getBorderElement(lx, ly);
1378
1379     DrawScreenElement(x, y, element);
1380     return;
1381   }
1382
1383   element = Feld[lx][ly];
1384   content = Store[lx][ly];
1385
1386   if (IS_MOVING(lx, ly))
1387   {
1388     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1389     boolean cut_mode = NO_CUTTING;
1390
1391     if (element == EL_QUICKSAND_EMPTYING ||
1392         element == EL_MAGIC_WALL_EMPTYING ||
1393         element == EL_BD_MAGIC_WALL_EMPTYING ||
1394         element == EL_AMOEBA_DROPPING)
1395       cut_mode = CUT_ABOVE;
1396     else if (element == EL_QUICKSAND_FILLING ||
1397              element == EL_MAGIC_WALL_FILLING ||
1398              element == EL_BD_MAGIC_WALL_FILLING)
1399       cut_mode = CUT_BELOW;
1400
1401     if (cut_mode == CUT_ABOVE)
1402       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
1403     else
1404       DrawScreenElement(x, y, EL_EMPTY);
1405
1406     if (horiz_move)
1407       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1408     else if (cut_mode == NO_CUTTING)
1409       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1410     else
1411       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1412
1413     if (content == EL_ACID)
1414       DrawLevelElementThruMask(lx, ly + 1, EL_ACID);
1415   }
1416   else if (IS_BLOCKED(lx, ly))
1417   {
1418     int oldx, oldy;
1419     int sx, sy;
1420     int horiz_move;
1421     boolean cut_mode = NO_CUTTING;
1422     int element_old, content_old;
1423
1424     Blocked2Moving(lx, ly, &oldx, &oldy);
1425     sx = SCREENX(oldx);
1426     sy = SCREENY(oldy);
1427     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1428                   MovDir[oldx][oldy] == MV_RIGHT);
1429
1430     element_old = Feld[oldx][oldy];
1431     content_old = Store[oldx][oldy];
1432
1433     if (element_old == EL_QUICKSAND_EMPTYING ||
1434         element_old == EL_MAGIC_WALL_EMPTYING ||
1435         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1436         element_old == EL_AMOEBA_DROPPING)
1437       cut_mode = CUT_ABOVE;
1438
1439     DrawScreenElement(x, y, EL_EMPTY);
1440
1441     if (horiz_move)
1442       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1443                                NO_CUTTING);
1444     else if (cut_mode == NO_CUTTING)
1445       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1446                                cut_mode);
1447     else
1448       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1449                                cut_mode);
1450   }
1451   else if (IS_DRAWABLE(element))
1452     DrawScreenElement(x, y, element);
1453   else
1454     DrawScreenElement(x, y, EL_EMPTY);
1455 }
1456
1457 void DrawLevelField(int x, int y)
1458 {
1459   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1460     DrawScreenField(SCREENX(x), SCREENY(y));
1461   else if (IS_MOVING(x, y))
1462   {
1463     int newx,newy;
1464
1465     Moving2Blocked(x, y, &newx, &newy);
1466     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1467       DrawScreenField(SCREENX(newx), SCREENY(newy));
1468   }
1469   else if (IS_BLOCKED(x, y))
1470   {
1471     int oldx, oldy;
1472
1473     Blocked2Moving(x, y, &oldx, &oldy);
1474     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1475       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1476   }
1477 }
1478
1479 void DrawMiniElement(int x, int y, int element)
1480 {
1481   int graphic;
1482
1483   graphic = el2edimg(element);
1484   DrawMiniGraphic(x, y, graphic);
1485 }
1486
1487 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1488 {
1489   int x = sx + scroll_x, y = sy + scroll_y;
1490
1491   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1492     DrawMiniElement(sx, sy, EL_EMPTY);
1493   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1494     DrawMiniElement(sx, sy, Feld[x][y]);
1495   else
1496     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
1497 }
1498
1499 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1500 {
1501   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1502   int mini_startx = src_bitmap->width * 3 / 4;
1503   int mini_starty = src_bitmap->height * 2 / 3;
1504   int src_x = mini_startx + graphic_info[graphic].src_x / 8;
1505   int src_y = mini_starty + graphic_info[graphic].src_y / 8;
1506
1507   *bitmap = src_bitmap;
1508   *x = src_x;
1509   *y = src_y;
1510 }
1511
1512 void DrawMicroElement(int xpos, int ypos, int element)
1513 {
1514   Bitmap *src_bitmap;
1515   int src_x, src_y;
1516   int graphic = el2preimg(element);
1517
1518   getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1519   BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
1520              xpos, ypos);
1521 }
1522
1523 void DrawLevel()
1524 {
1525   int x,y;
1526
1527   SetDrawBackgroundMask(REDRAW_NONE);
1528   ClearWindow();
1529
1530   for(x=BX1; x<=BX2; x++)
1531     for(y=BY1; y<=BY2; y++)
1532       DrawScreenField(x, y);
1533
1534   redraw_mask |= REDRAW_FIELD;
1535 }
1536
1537 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1538 {
1539   int x,y;
1540
1541   for(x=0; x<size_x; x++)
1542     for(y=0; y<size_y; y++)
1543       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1544
1545   redraw_mask |= REDRAW_FIELD;
1546 }
1547
1548 static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
1549 {
1550   int x, y;
1551
1552   DrawBackground(xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
1553
1554   if (lev_fieldx < STD_LEV_FIELDX)
1555     xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
1556   if (lev_fieldy < STD_LEV_FIELDY)
1557     ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
1558
1559   xpos += MICRO_TILEX;
1560   ypos += MICRO_TILEY;
1561
1562   for(x=-1; x<=STD_LEV_FIELDX; x++)
1563   {
1564     for(y=-1; y<=STD_LEV_FIELDY; y++)
1565     {
1566       int lx = from_x + x, ly = from_y + y;
1567
1568       if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
1569         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1570                          level.field[lx][ly]);
1571       else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
1572                && BorderElement != EL_EMPTY)
1573         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1574                          getBorderElement(lx, ly));
1575     }
1576   }
1577
1578   redraw_mask |= REDRAW_MICROLEVEL;
1579 }
1580
1581 #define MICROLABEL_EMPTY                0
1582 #define MICROLABEL_LEVEL_NAME           1
1583 #define MICROLABEL_CREATED_BY           2
1584 #define MICROLABEL_LEVEL_AUTHOR         3
1585 #define MICROLABEL_IMPORTED_FROM        4
1586 #define MICROLABEL_LEVEL_IMPORT_INFO    5
1587
1588 static void DrawMicroLevelLabelExt(int mode)
1589 {
1590   char label_text[MAX_OUTPUT_LINESIZE + 1];
1591   int max_len_label_text;
1592   int font_nr = FONT_TEXT_2;
1593
1594   if (mode == MICROLABEL_CREATED_BY || mode == MICROLABEL_IMPORTED_FROM)
1595     font_nr = FONT_TEXT_3;
1596
1597   max_len_label_text = SXSIZE / getFontWidth(font_nr);
1598
1599   DrawBackground(SX, MICROLABEL_YPOS, SXSIZE, getFontHeight(font_nr));
1600
1601   strncpy(label_text, (mode == MICROLABEL_LEVEL_NAME ? level.name :
1602                        mode == MICROLABEL_CREATED_BY ? "created by" :
1603                        mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
1604                        mode == MICROLABEL_IMPORTED_FROM ? "imported from" :
1605                        mode == MICROLABEL_LEVEL_IMPORT_INFO ?
1606                        leveldir_current->imported_from : ""),
1607           max_len_label_text);
1608   label_text[max_len_label_text] = '\0';
1609
1610   if (strlen(label_text) > 0)
1611   {
1612     int text_width = strlen(label_text) * getFontWidth(font_nr);
1613     int lxpos = SX + (SXSIZE - text_width) / 2;
1614     int lypos = MICROLABEL_YPOS;
1615
1616     DrawText(lxpos, lypos, label_text, font_nr);
1617   }
1618
1619   redraw_mask |= REDRAW_MICROLEVEL;
1620 }
1621
1622 void DrawMicroLevel(int xpos, int ypos, boolean restart)
1623 {
1624   static unsigned long scroll_delay = 0;
1625   static unsigned long label_delay = 0;
1626   static int from_x, from_y, scroll_direction;
1627   static int label_state, label_counter;
1628   int last_game_status = game_status;   /* save current game status */
1629
1630   /* force PREVIEW font on preview level */
1631   game_status = GAME_MODE_PSEUDO_PREVIEW;
1632
1633   if (restart)
1634   {
1635     from_x = from_y = 0;
1636     scroll_direction = MV_RIGHT;
1637     label_state = 1;
1638     label_counter = 0;
1639
1640     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1641     DrawMicroLevelLabelExt(label_state);
1642
1643     /* initialize delay counters */
1644     DelayReached(&scroll_delay, 0);
1645     DelayReached(&label_delay, 0);
1646
1647     if (leveldir_current->name)
1648     {
1649       int len = strlen(leveldir_current->name);
1650       int lxpos = SX + (SXSIZE - len * getFontWidth(FONT_TEXT_1)) / 2;
1651       int lypos = SY + 352;
1652
1653       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
1654     }
1655
1656     game_status = last_game_status;     /* restore current game status */
1657
1658     return;
1659   }
1660
1661   /* scroll micro level, if needed */
1662   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1663       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1664   {
1665     switch (scroll_direction)
1666     {
1667       case MV_LEFT:
1668         if (from_x > 0)
1669           from_x--;
1670         else
1671           scroll_direction = MV_UP;
1672         break;
1673
1674       case MV_RIGHT:
1675         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1676           from_x++;
1677         else
1678           scroll_direction = MV_DOWN;
1679         break;
1680
1681       case MV_UP:
1682         if (from_y > 0)
1683           from_y--;
1684         else
1685           scroll_direction = MV_RIGHT;
1686         break;
1687
1688       case MV_DOWN:
1689         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1690           from_y++;
1691         else
1692           scroll_direction = MV_LEFT;
1693         break;
1694
1695       default:
1696         break;
1697     }
1698
1699     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1700   }
1701
1702   /* redraw micro level label, if needed */
1703   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
1704       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
1705       strcmp(level.author, leveldir_current->name) != 0 &&
1706       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1707   {
1708     int max_label_counter = 23;
1709
1710     if (leveldir_current->imported_from != NULL)
1711       max_label_counter += 14;
1712
1713     label_counter = (label_counter + 1) % max_label_counter;
1714     label_state = (label_counter >= 0 && label_counter <= 7 ?
1715                    MICROLABEL_LEVEL_NAME :
1716                    label_counter >= 9 && label_counter <= 12 ?
1717                    MICROLABEL_CREATED_BY :
1718                    label_counter >= 14 && label_counter <= 21 ?
1719                    MICROLABEL_LEVEL_AUTHOR :
1720                    label_counter >= 23 && label_counter <= 26 ?
1721                    MICROLABEL_IMPORTED_FROM :
1722                    label_counter >= 28 && label_counter <= 35 ?
1723                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
1724     DrawMicroLevelLabelExt(label_state);
1725   }
1726
1727   game_status = last_game_status;       /* restore current game status */
1728 }
1729
1730 int REQ_in_range(int x, int y)
1731 {
1732   if (y > DY+249 && y < DY+278)
1733   {
1734     if (x > DX+1 && x < DX+48)
1735       return 1;
1736     else if (x > DX+51 && x < DX+98) 
1737       return 2;
1738   }
1739   return 0;
1740 }
1741
1742 #define MAX_REQUEST_LINES               13
1743 #define MAX_REQUEST_LINE_LEN            7
1744
1745 boolean Request(char *text, unsigned int req_state)
1746 {
1747   int mx, my, ty, result = -1;
1748   unsigned int old_door_state;
1749   int last_game_status = game_status;   /* save current game status */
1750
1751 #if defined(PLATFORM_UNIX)
1752   /* pause network game while waiting for request to answer */
1753   if (options.network &&
1754       game_status == GAME_MODE_PLAYING &&
1755       req_state & REQUEST_WAIT_FOR)
1756     SendToServer_PausePlaying();
1757 #endif
1758
1759   old_door_state = GetDoorState();
1760
1761   /* simulate releasing mouse button over last gadget, if still pressed */
1762   if (button_status)
1763     HandleGadgets(-1, -1, 0);
1764
1765   UnmapAllGadgets();
1766
1767   CloseDoor(DOOR_CLOSE_1);
1768
1769   /* save old door content */
1770   BlitBitmap(bitmap_db_door, bitmap_db_door,
1771              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
1772              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
1773
1774   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1775
1776   /* clear door drawing field */
1777   DrawBackground(DX, DY, DXSIZE, DYSIZE);
1778
1779   /* force DOOR font on preview level */
1780   game_status = GAME_MODE_PSEUDO_DOOR;
1781
1782   /* write text for request */
1783   for(ty=0; ty < MAX_REQUEST_LINES; ty++)
1784   {
1785     char text_line[MAX_REQUEST_LINE_LEN + 1];
1786     int tx, tl, tc;
1787
1788     if (!*text)
1789       break;
1790
1791     for(tl=0,tx=0; tx < MAX_REQUEST_LINE_LEN; tl++,tx++)
1792     {
1793       tc = *(text + tx);
1794       if (!tc || tc == ' ')
1795         break;
1796     }
1797
1798     if (!tl)
1799     { 
1800       text++; 
1801       ty--; 
1802       continue; 
1803     }
1804
1805     strncpy(text_line, text, tl);
1806     text_line[tl] = 0;
1807
1808     DrawText(DX + (DXSIZE - tl * getFontWidth(FONT_TEXT_2)) / 2,
1809              DY + 8 + ty * (getFontHeight(FONT_TEXT_2) + 2),
1810              text_line, FONT_TEXT_2);
1811
1812     text += tl + (tc == ' ' ? 1 : 0);
1813   }
1814
1815   game_status = last_game_status;       /* restore current game status */
1816
1817   if (req_state & REQ_ASK)
1818   {
1819     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
1820     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
1821   }
1822   else if (req_state & REQ_CONFIRM)
1823   {
1824     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
1825   }
1826   else if (req_state & REQ_PLAYER)
1827   {
1828     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
1829     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
1830     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
1831     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
1832   }
1833
1834   /* copy request gadgets to door backbuffer */
1835   BlitBitmap(drawto, bitmap_db_door,
1836              DX, DY, DXSIZE, DYSIZE,
1837              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1838
1839   OpenDoor(DOOR_OPEN_1);
1840
1841 #if 0
1842   ClearEventQueue();
1843 #endif
1844
1845   if (!(req_state & REQUEST_WAIT_FOR))
1846   {
1847     SetDrawBackgroundMask(REDRAW_FIELD);
1848
1849     return FALSE;
1850   }
1851
1852   if (game_status != GAME_MODE_MAIN)
1853     InitAnimation();
1854
1855   button_status = MB_RELEASED;
1856
1857   request_gadget_id = -1;
1858
1859   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1860
1861   while(result < 0)
1862   {
1863     if (PendingEvent())
1864     {
1865       Event event;
1866
1867       NextEvent(&event);
1868
1869       switch(event.type)
1870       {
1871         case EVENT_BUTTONPRESS:
1872         case EVENT_BUTTONRELEASE:
1873         case EVENT_MOTIONNOTIFY:
1874         {
1875           if (event.type == EVENT_MOTIONNOTIFY)
1876           {
1877             if (!PointerInWindow(window))
1878               continue; /* window and pointer are on different screens */
1879
1880             if (!button_status)
1881               continue;
1882
1883             motion_status = TRUE;
1884             mx = ((MotionEvent *) &event)->x;
1885             my = ((MotionEvent *) &event)->y;
1886           }
1887           else
1888           {
1889             motion_status = FALSE;
1890             mx = ((ButtonEvent *) &event)->x;
1891             my = ((ButtonEvent *) &event)->y;
1892             if (event.type == EVENT_BUTTONPRESS)
1893               button_status = ((ButtonEvent *) &event)->button;
1894             else
1895               button_status = MB_RELEASED;
1896           }
1897
1898           /* this sets 'request_gadget_id' */
1899           HandleGadgets(mx, my, button_status);
1900
1901           switch(request_gadget_id)
1902           {
1903             case TOOL_CTRL_ID_YES:
1904               result = TRUE;
1905               break;
1906             case TOOL_CTRL_ID_NO:
1907               result = FALSE;
1908               break;
1909             case TOOL_CTRL_ID_CONFIRM:
1910               result = TRUE | FALSE;
1911               break;
1912
1913             case TOOL_CTRL_ID_PLAYER_1:
1914               result = 1;
1915               break;
1916             case TOOL_CTRL_ID_PLAYER_2:
1917               result = 2;
1918               break;
1919             case TOOL_CTRL_ID_PLAYER_3:
1920               result = 3;
1921               break;
1922             case TOOL_CTRL_ID_PLAYER_4:
1923               result = 4;
1924               break;
1925
1926             default:
1927               break;
1928           }
1929
1930           break;
1931         }
1932
1933         case EVENT_KEYPRESS:
1934           switch(GetEventKey((KeyEvent *)&event, TRUE))
1935           {
1936             case KSYM_Return:
1937               result = 1;
1938               break;
1939
1940             case KSYM_Escape:
1941               result = 0;
1942               break;
1943
1944             default:
1945               break;
1946           }
1947           if (req_state & REQ_PLAYER)
1948             result = 0;
1949           break;
1950
1951         case EVENT_KEYRELEASE:
1952           ClearPlayerAction();
1953           break;
1954
1955         default:
1956           HandleOtherEvents(&event);
1957           break;
1958       }
1959     }
1960     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
1961     {
1962       int joy = AnyJoystick();
1963
1964       if (joy & JOY_BUTTON_1)
1965         result = 1;
1966       else if (joy & JOY_BUTTON_2)
1967         result = 0;
1968     }
1969
1970     DoAnimation();
1971
1972     /* don't eat all CPU time */
1973     Delay(10);
1974   }
1975
1976   if (game_status != GAME_MODE_MAIN)
1977     StopAnimation();
1978
1979   UnmapToolButtons();
1980
1981   if (!(req_state & REQ_STAY_OPEN))
1982   {
1983     CloseDoor(DOOR_CLOSE_1);
1984
1985     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
1986     {
1987       BlitBitmap(bitmap_db_door, bitmap_db_door,
1988                  DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
1989                  DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
1990       OpenDoor(DOOR_OPEN_1);
1991     }
1992   }
1993
1994   RemapAllGadgets();
1995
1996   SetDrawBackgroundMask(REDRAW_FIELD);
1997
1998 #if defined(PLATFORM_UNIX)
1999   /* continue network game after request */
2000   if (options.network &&
2001       game_status == GAME_MODE_PLAYING &&
2002       req_state & REQUEST_WAIT_FOR)
2003     SendToServer_ContinuePlaying();
2004 #endif
2005
2006   return result;
2007 }
2008
2009 unsigned int OpenDoor(unsigned int door_state)
2010 {
2011   unsigned int new_door_state;
2012
2013   if (door_state & DOOR_COPY_BACK)
2014   {
2015     BlitBitmap(bitmap_db_door, bitmap_db_door,
2016                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
2017                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2018     door_state &= ~DOOR_COPY_BACK;
2019   }
2020
2021   new_door_state = MoveDoor(door_state);
2022
2023   return(new_door_state);
2024 }
2025
2026 unsigned int CloseDoor(unsigned int door_state)
2027 {
2028   unsigned int new_door_state;
2029
2030   BlitBitmap(backbuffer, bitmap_db_door,
2031              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2032   BlitBitmap(backbuffer, bitmap_db_door,
2033              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2034
2035   new_door_state = MoveDoor(door_state);
2036
2037   return(new_door_state);
2038 }
2039
2040 unsigned int GetDoorState()
2041 {
2042   return MoveDoor(DOOR_GET_STATE);
2043 }
2044
2045 unsigned int SetDoorState(unsigned int door_state)
2046 {
2047   return MoveDoor(door_state | DOOR_SET_STATE);
2048 }
2049
2050 unsigned int MoveDoor(unsigned int door_state)
2051 {
2052   static int door1 = DOOR_OPEN_1;
2053   static int door2 = DOOR_CLOSE_2;
2054   static unsigned long door_delay = 0;
2055   int x, start, stepsize = door.step_offset;
2056   unsigned long door_delay_value = door.step_delay;
2057
2058   if (door_state == DOOR_GET_STATE)
2059     return(door1 | door2);
2060
2061   if (door_state & DOOR_SET_STATE)
2062   {
2063     if (door_state & DOOR_ACTION_1)
2064       door1 = door_state & DOOR_ACTION_1;
2065     if (door_state & DOOR_ACTION_2)
2066       door2 = door_state & DOOR_ACTION_2;
2067
2068     return(door1 | door2);
2069   }
2070
2071   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2072     door_state &= ~DOOR_OPEN_1;
2073   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2074     door_state &= ~DOOR_CLOSE_1;
2075   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2076     door_state &= ~DOOR_OPEN_2;
2077   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2078     door_state &= ~DOOR_CLOSE_2;
2079
2080   if (setup.quick_doors)
2081   {
2082     stepsize = 20;
2083     door_delay_value = 0;
2084
2085     StopSound(SND_DOOR_OPENING);
2086     StopSound(SND_DOOR_CLOSING);
2087   }
2088
2089   if (global.autoplay_leveldir)
2090   {
2091     door_state |= DOOR_NO_DELAY;
2092     door_state &= ~DOOR_CLOSE_ALL;
2093   }
2094
2095   if (door_state & DOOR_ACTION)
2096   {
2097     if (!(door_state & DOOR_NO_DELAY))
2098     {
2099       /* opening door sound has priority over simultaneously closing door */
2100       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2101         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2102       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2103         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2104     }
2105
2106     start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2107
2108     for(x=start; x<=DXSIZE; x+=stepsize)
2109     {
2110       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2111       GC gc = bitmap->stored_clip_gc;
2112
2113       if (!(door_state & DOOR_NO_DELAY))
2114         WaitUntilDelayReached(&door_delay, door_delay_value);
2115
2116       if (door_state & DOOR_ACTION_1)
2117       {
2118         int i = (door_state & DOOR_OPEN_1 ? DXSIZE-x : x);
2119         int j = (DXSIZE - i) / 3;
2120
2121         BlitBitmap(bitmap_db_door, drawto,
2122                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i/2,
2123                    DXSIZE,DYSIZE - i/2, DX, DY);
2124
2125         ClearRectangle(drawto, DX, DY + DYSIZE - i/2, DXSIZE,i/2);
2126
2127         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2128         BlitBitmapMasked(bitmap, drawto,
2129                          DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2130                          DX + DXSIZE - i, DY + j);
2131         BlitBitmapMasked(bitmap, drawto,
2132                          DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2133                          DX + DXSIZE - i, DY + 140 + j);
2134         SetClipOrigin(bitmap, gc, DX - DXSIZE + i, DY - (DOOR_GFX_PAGEY1 + j));
2135         BlitBitmapMasked(bitmap, drawto,
2136                          DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2137                          DX, DY);
2138         BlitBitmapMasked(bitmap, drawto,
2139                          DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2140                          DX, DY + 140 - j);
2141
2142         BlitBitmapMasked(bitmap, drawto,
2143                          DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2144                          DX, DY + 77 - j);
2145         BlitBitmapMasked(bitmap, drawto,
2146                          DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2147                          DX, DY + 203 - j);
2148         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2149         BlitBitmapMasked(bitmap, drawto,
2150                          DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2151                          DX + DXSIZE - i, DY + 77 + j);
2152         BlitBitmapMasked(bitmap, drawto,
2153                          DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2154                          DX + DXSIZE - i, DY + 203 + j);
2155
2156         redraw_mask |= REDRAW_DOOR_1;
2157       }
2158
2159       if (door_state & DOOR_ACTION_2)
2160       {
2161         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - x : x);
2162         int j = (VXSIZE - i) / 3;
2163
2164         BlitBitmap(bitmap_db_door, drawto,
2165                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i/2,
2166                    VXSIZE, VYSIZE - i/2, VX, VY);
2167
2168         ClearRectangle(drawto, VX, VY + VYSIZE-i/2, VXSIZE, i/2);
2169
2170         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2171         BlitBitmapMasked(bitmap, drawto,
2172                          VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2173                          VX + VXSIZE-i, VY+j);
2174         SetClipOrigin(bitmap, gc,
2175                       VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2176         BlitBitmapMasked(bitmap, drawto,
2177                          VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2178                          VX, VY);
2179
2180         BlitBitmapMasked(bitmap, drawto,
2181                          VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2182                          i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2183         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2184         BlitBitmapMasked(bitmap, drawto,
2185                          VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2186                          i, VYSIZE / 2 - j,
2187                          VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2188
2189         redraw_mask |= REDRAW_DOOR_2;
2190       }
2191
2192       BackToFront();
2193
2194       if (game_status == GAME_MODE_MAIN)
2195         DoAnimation();
2196     }
2197   }
2198
2199   if (setup.quick_doors)
2200   {
2201     StopSound(SND_DOOR_OPENING);
2202     StopSound(SND_DOOR_CLOSING);
2203   }
2204
2205   if (door_state & DOOR_ACTION_1)
2206     door1 = door_state & DOOR_ACTION_1;
2207   if (door_state & DOOR_ACTION_2)
2208     door2 = door_state & DOOR_ACTION_2;
2209
2210   return (door1 | door2);
2211 }
2212
2213 void DrawSpecialEditorDoor()
2214 {
2215   /* draw bigger toolbox window */
2216   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2217              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2218              EX - 4, EY - 12);
2219   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2220              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2221              EX - 4, EY - 4);
2222
2223   redraw_mask |= REDRAW_ALL;
2224 }
2225
2226 void UndrawSpecialEditorDoor()
2227 {
2228   /* draw normal tape recorder window */
2229   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2230              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2231              EX - 4, EY - 12);
2232
2233   redraw_mask |= REDRAW_ALL;
2234 }
2235
2236
2237 /* ---------- new tool button stuff ---------------------------------------- */
2238
2239 /* graphic position values for tool buttons */
2240 #define TOOL_BUTTON_YES_XPOS            2
2241 #define TOOL_BUTTON_YES_YPOS            250
2242 #define TOOL_BUTTON_YES_GFX_YPOS        0
2243 #define TOOL_BUTTON_YES_XSIZE           46
2244 #define TOOL_BUTTON_YES_YSIZE           28
2245 #define TOOL_BUTTON_NO_XPOS             52
2246 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2247 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2248 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2249 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2250 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2251 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2252 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2253 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2254 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2255 #define TOOL_BUTTON_PLAYER_XSIZE        30
2256 #define TOOL_BUTTON_PLAYER_YSIZE        30
2257 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2258 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2259 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2260 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2261 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2262                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2263 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2264                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2265 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2266                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2267 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2268                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2269 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2270                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2271 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2272                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2273 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2274                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2275 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2276                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2277
2278 static struct
2279 {
2280   int xpos, ypos;
2281   int x, y;
2282   int width, height;
2283   int gadget_id;
2284   char *infotext;
2285 } toolbutton_info[NUM_TOOL_BUTTONS] =
2286 {
2287   {
2288     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2289     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2290     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2291     TOOL_CTRL_ID_YES,
2292     "yes"
2293   },
2294   {
2295     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2296     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2297     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2298     TOOL_CTRL_ID_NO,
2299     "no"
2300   },
2301   {
2302     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2303     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2304     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2305     TOOL_CTRL_ID_CONFIRM,
2306     "confirm"
2307   },
2308   {
2309     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2310     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2311     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2312     TOOL_CTRL_ID_PLAYER_1,
2313     "player 1"
2314   },
2315   {
2316     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2317     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2318     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2319     TOOL_CTRL_ID_PLAYER_2,
2320     "player 2"
2321   },
2322   {
2323     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2324     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2325     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2326     TOOL_CTRL_ID_PLAYER_3,
2327     "player 3"
2328   },
2329   {
2330     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2331     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2332     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2333     TOOL_CTRL_ID_PLAYER_4,
2334     "player 4"
2335   }
2336 };
2337
2338 void CreateToolButtons()
2339 {
2340   int i;
2341
2342   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2343   {
2344     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2345     Bitmap *deco_bitmap = None;
2346     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2347     struct GadgetInfo *gi;
2348     unsigned long event_mask;
2349     int gd_xoffset, gd_yoffset;
2350     int gd_x1, gd_x2, gd_y;
2351     int id = i;
2352
2353     event_mask = GD_EVENT_RELEASED;
2354
2355     gd_xoffset = toolbutton_info[i].xpos;
2356     gd_yoffset = toolbutton_info[i].ypos;
2357     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2358     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2359     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2360
2361     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2362     {
2363       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2364
2365       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2366                            &deco_bitmap, &deco_x, &deco_y);
2367       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2368       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2369     }
2370
2371     gi = CreateGadget(GDI_CUSTOM_ID, id,
2372                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2373                       GDI_X, DX + toolbutton_info[i].x,
2374                       GDI_Y, DY + toolbutton_info[i].y,
2375                       GDI_WIDTH, toolbutton_info[i].width,
2376                       GDI_HEIGHT, toolbutton_info[i].height,
2377                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2378                       GDI_STATE, GD_BUTTON_UNPRESSED,
2379                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2380                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2381                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2382                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2383                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2384                       GDI_DECORATION_SHIFTING, 1, 1,
2385                       GDI_EVENT_MASK, event_mask,
2386                       GDI_CALLBACK_ACTION, HandleToolButtons,
2387                       GDI_END);
2388
2389     if (gi == NULL)
2390       Error(ERR_EXIT, "cannot create gadget");
2391
2392     tool_gadget[id] = gi;
2393   }
2394 }
2395
2396 void FreeToolButtons()
2397 {
2398   int i;
2399
2400   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2401     FreeGadget(tool_gadget[i]);
2402 }
2403
2404 static void UnmapToolButtons()
2405 {
2406   int i;
2407
2408   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2409     UnmapGadget(tool_gadget[i]);
2410 }
2411
2412 static void HandleToolButtons(struct GadgetInfo *gi)
2413 {
2414   request_gadget_id = gi->custom_id;
2415 }
2416
2417 int get_next_element(int element)
2418 {
2419   switch(element)
2420   {
2421     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2422     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2423     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2424     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2425     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2426     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2427     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2428
2429     default:                            return element;
2430   }
2431 }
2432
2433 int el_act_dir2img(int element, int action, int direction)
2434 {
2435   element = GFX_ELEMENT(element);
2436   direction = MV_DIR_BIT(direction);
2437
2438   return element_info[element].direction_graphic[action][direction];
2439 }
2440
2441 int el_act2img(int element, int action)
2442 {
2443   element = GFX_ELEMENT(element);
2444
2445   return element_info[element].graphic[action];
2446 }
2447
2448 int el_dir2img(int element, int direction)
2449 {
2450   element = GFX_ELEMENT(element);
2451
2452   return el_act_dir2img(element, ACTION_DEFAULT, direction);
2453 }
2454
2455 int el2img(int element)
2456 {
2457   element = GFX_ELEMENT(element);
2458
2459   return element_info[element].graphic[ACTION_DEFAULT];
2460 }
2461
2462 int el2edimg(int element)
2463 {
2464   element = GFX_ELEMENT(element);
2465
2466   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
2467 }
2468
2469 int el2preimg(int element)
2470 {
2471   element = GFX_ELEMENT(element);
2472
2473   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
2474 }