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