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