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