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