rnd-20030426-2-src
[rocksndiamonds.git] / src / tools.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * tools.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "game.h"
18 #include "events.h"
19 #include "cartoons.h"
20 #include "network.h"
21 #include "tape.h"
22
23 /* tool button identifiers */
24 #define TOOL_CTRL_ID_YES        0
25 #define TOOL_CTRL_ID_NO         1
26 #define TOOL_CTRL_ID_CONFIRM    2
27 #define TOOL_CTRL_ID_PLAYER_1   3
28 #define TOOL_CTRL_ID_PLAYER_2   4
29 #define TOOL_CTRL_ID_PLAYER_3   5
30 #define TOOL_CTRL_ID_PLAYER_4   6
31
32 #define NUM_TOOL_BUTTONS        7
33
34 /* forward declaration for internal use */
35 static void UnmapToolButtons();
36 static void HandleToolButtons(struct GadgetInfo *);
37
38 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
39 static int request_gadget_id = -1;
40
41 void SetDrawtoField(int mode)
42 {
43   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
44   {
45     FX = TILEX;
46     FY = TILEY;
47     BX1 = -1;
48     BY1 = -1;
49     BX2 = SCR_FIELDX;
50     BY2 = SCR_FIELDY;
51     redraw_x1 = 1;
52     redraw_y1 = 1;
53
54     drawto_field = fieldbuffer;
55   }
56   else  /* DRAW_DIRECT, DRAW_BACKBUFFER */
57   {
58     FX = SX;
59     FY = SY;
60     BX1 = 0;
61     BY1 = 0;
62     BX2 = SCR_FIELDX - 1;
63     BY2 = SCR_FIELDY - 1;
64     redraw_x1 = 0;
65     redraw_y1 = 0;
66
67     drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
68   }
69 }
70
71 void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
72 {
73   if (game_status == GAME_MODE_PLAYING)
74   {
75     if (force_redraw)
76     {
77       x = gfx.sx - TILEX;
78       y = gfx.sy - TILEY;
79       width = gfx.sxsize + 2 * TILEX;
80       height = gfx.sysize + 2 * TILEY;
81     }
82
83     if (force_redraw || setup.direct_draw)
84     {
85       int xx, yy;
86       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
87       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
88
89       if (setup.direct_draw)
90         SetDrawtoField(DRAW_BACKBUFFER);
91
92       for(xx=BX1; xx<=BX2; xx++)
93         for(yy=BY1; yy<=BY2; yy++)
94           if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
95             DrawScreenField(xx, yy);
96       DrawAllPlayers();
97
98       if (setup.direct_draw)
99         SetDrawtoField(DRAW_DIRECT);
100     }
101
102     if (setup.soft_scrolling)
103     {
104       int fx = FX, fy = FY;
105
106       fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
107       fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
108
109       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
110     }
111   }
112
113   BlitBitmap(drawto, window, x, y, width, height, x, y);
114 }
115
116 void BackToFront()
117 {
118   int x,y;
119   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
120
121   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
122     redraw_mask &= ~REDRAW_MAIN;
123
124   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
125     redraw_mask |= REDRAW_FIELD;
126
127   if (redraw_mask & REDRAW_FIELD)
128     redraw_mask &= ~REDRAW_TILES;
129
130   if (redraw_mask == REDRAW_NONE)
131     return;
132
133   if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
134   {
135     static boolean last_frame_skipped = FALSE;
136     boolean skip_even_when_not_scrolling = TRUE;
137     boolean just_scrolling = (ScreenMovDir != 0);
138     boolean verbose = FALSE;
139
140     if (global.fps_slowdown_factor > 1 &&
141         (FrameCounter % global.fps_slowdown_factor) &&
142         (just_scrolling || skip_even_when_not_scrolling))
143     {
144       redraw_mask &= ~REDRAW_MAIN;
145
146       last_frame_skipped = TRUE;
147
148       if (verbose)
149         printf("FRAME SKIPPED\n");
150     }
151     else
152     {
153       if (last_frame_skipped)
154         redraw_mask |= REDRAW_FIELD;
155
156       last_frame_skipped = FALSE;
157
158       if (verbose)
159         printf("frame not skipped\n");
160     }
161   }
162
163   /* synchronize X11 graphics at this point; if we would synchronize the
164      display immediately after the buffer switching (after the XFlush),
165      this could mean that we have to wait for the graphics to complete,
166      although we could go on doing calculations for the next frame */
167
168   SyncDisplay();
169
170   if (redraw_mask & REDRAW_ALL)
171   {
172     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
173     redraw_mask = 0;
174   }
175
176   if (redraw_mask & REDRAW_FIELD)
177   {
178     if (game_status != GAME_MODE_PLAYING ||
179         redraw_mask & REDRAW_FROM_BACKBUFFER)
180     {
181       BlitBitmap(backbuffer, window,
182                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
183     }
184     else
185     {
186       int fx = FX, fy = FY;
187
188       if (setup.soft_scrolling)
189       {
190         fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
191         fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
192       }
193
194       if (setup.soft_scrolling ||
195           ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
196           ABS(ScreenMovPos) == ScrollStepSize ||
197           redraw_tiles > REDRAWTILES_THRESHOLD)
198       {
199         BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
200
201 #ifdef DEBUG
202 #if 0
203         printf("redrawing all (ScreenGfxPos == %d) because %s\n",
204                ScreenGfxPos,
205                (setup.soft_scrolling ?
206                 "setup.soft_scrolling" :
207                 ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
208                 "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
209                 ABS(ScreenGfxPos) == ScrollStepSize ?
210                 "ABS(ScreenGfxPos) == ScrollStepSize" :
211                 "redraw_tiles > REDRAWTILES_THRESHOLD"));
212 #endif
213 #endif
214       }
215     }
216
217     redraw_mask &= ~REDRAW_MAIN;
218   }
219
220   if (redraw_mask & REDRAW_DOORS)
221   {
222     if (redraw_mask & REDRAW_DOOR_1)
223       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
224     if (redraw_mask & REDRAW_DOOR_2)
225     {
226       if ((redraw_mask & REDRAW_DOOR_2) == REDRAW_DOOR_2)
227         BlitBitmap(backbuffer, window, VX,VY, VXSIZE,VYSIZE, VX,VY);
228       else
229       {
230         if (redraw_mask & REDRAW_VIDEO_1)
231           BlitBitmap(backbuffer, window,
232                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS,
233                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
234                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS);
235         if (redraw_mask & REDRAW_VIDEO_2)
236           BlitBitmap(backbuffer, window,
237                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS,
238                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
239                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS);
240         if (redraw_mask & REDRAW_VIDEO_3)
241           BlitBitmap(backbuffer, window,
242                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS,
243                      VIDEO_CONTROL_XSIZE,VIDEO_CONTROL_YSIZE,
244                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS);
245       }
246     }
247
248     if (redraw_mask & REDRAW_DOOR_3)
249       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
250
251     redraw_mask &= ~REDRAW_DOORS;
252   }
253
254   if (redraw_mask & REDRAW_MICROLEVEL)
255   {
256     BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
257                SX, SY + 10 * TILEY);
258
259     redraw_mask &= ~REDRAW_MICROLEVEL;
260   }
261
262   if (redraw_mask & REDRAW_TILES)
263   {
264     for(x=0; x<SCR_FIELDX; x++)
265       for(y=0; y<SCR_FIELDY; y++)
266         if (redraw[redraw_x1 + x][redraw_y1 + y])
267           BlitBitmap(buffer, window,
268                      FX + x * TILEX, FX + y * TILEY, TILEX, TILEY,
269                      SX + x * TILEX, SY + y * TILEY);
270   }
271
272   if (redraw_mask & REDRAW_FPS)         /* display frames per second */
273   {
274     char text[100];
275     char info1[100];
276
277     sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
278     if (!global.fps_slowdown)
279       info1[0] = '\0';
280
281     sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
282     DrawTextExt(window, SX, SY, text, FONT_TEXT_2, BLIT_OPAQUE);
283   }
284
285   FlushDisplay();
286
287   for(x=0; x<MAX_BUF_XSIZE; x++)
288     for(y=0; y<MAX_BUF_YSIZE; y++)
289       redraw[x][y] = 0;
290   redraw_tiles = 0;
291   redraw_mask = REDRAW_NONE;
292 }
293
294 void FadeToFront()
295 {
296 #if 0
297   long fading_delay = 300;
298
299   if (setup.fading && (redraw_mask & REDRAW_FIELD))
300   {
301 #endif
302
303 #if 0
304     int x,y;
305
306     ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
307     FlushDisplay();
308
309     for(i=0;i<2*FULL_SYSIZE;i++)
310     {
311       for(y=0;y<FULL_SYSIZE;y++)
312       {
313         BlitBitmap(backbuffer, window,
314                    REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
315       }
316       FlushDisplay();
317       Delay(10);
318     }
319 #endif
320
321 #if 0
322     for(i=1;i<FULL_SYSIZE;i+=2)
323       BlitBitmap(backbuffer, window,
324                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
325     FlushDisplay();
326     Delay(fading_delay);
327 #endif
328
329 #if 0
330     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
331     BlitBitmapMasked(backbuffer, window,
332                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
333                      REAL_SX,REAL_SY);
334     FlushDisplay();
335     Delay(fading_delay);
336
337     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
338     BlitBitmapMasked(backbuffer, window,
339                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
340                      REAL_SX,REAL_SY);
341     FlushDisplay();
342     Delay(fading_delay);
343
344     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
345     BlitBitmapMasked(backbuffer, window,
346                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
347                      REAL_SX,REAL_SY);
348     FlushDisplay();
349     Delay(fading_delay);
350
351     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
352     BlitBitmapMasked(backbuffer, window,
353                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
354                      REAL_SX,REAL_SY);
355     FlushDisplay();
356     Delay(fading_delay);
357
358     redraw_mask &= ~REDRAW_MAIN;
359   }
360 #endif
361
362   BackToFront();
363 }
364
365 void SetMainBackgroundImage(int graphic)
366 {
367   SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
368                           graphic_info[graphic].bitmap ?
369                           graphic_info[graphic].bitmap :
370                           graphic_info[IMG_BACKGROUND].bitmap);
371 }
372
373 void SetDoorBackgroundImage(int graphic)
374 {
375   SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
376                           graphic_info[graphic].bitmap ?
377                           graphic_info[graphic].bitmap :
378                           graphic_info[IMG_BACKGROUND].bitmap);
379 }
380
381 void DrawBackground(int dest_x, int dest_y, int width, int height)
382 {
383   ClearRectangleOnBackground(backbuffer, dest_x, dest_y, width, height);
384
385   redraw_mask |= REDRAW_FIELD;
386 }
387
388 void ClearWindow()
389 {
390   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
391
392   if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
393   {
394     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
395     SetDrawtoField(DRAW_BUFFERED);
396   }
397   else
398     SetDrawtoField(DRAW_BACKBUFFER);
399
400   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
401   {
402     ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
403     SetDrawtoField(DRAW_DIRECT);
404   }
405 }
406
407 void MarkTileDirty(int x, int y)
408 {
409   int xx = redraw_x1 + x;
410   int yy = redraw_y1 + y;
411
412   if (!redraw[xx][yy])
413     redraw_tiles++;
414
415   redraw[xx][yy] = TRUE;
416   redraw_mask |= REDRAW_TILES;
417 }
418
419 void SetBorderElement()
420 {
421   int x, y;
422
423   BorderElement = EL_EMPTY;
424
425   for(y=0; y<lev_fieldy && BorderElement == EL_EMPTY; y++)
426   {
427     for(x=0; x<lev_fieldx; x++)
428     {
429       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
430         BorderElement = EL_STEELWALL;
431
432       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
433         x = lev_fieldx - 2;
434     }
435   }
436 }
437
438 void SetRandomAnimationValue(int x, int y)
439 {
440   gfx.anim_random_frame = GfxRandom[x][y];
441 }
442
443 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
444 {
445   /* animation synchronized with global frame counter, not move position */
446   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
447     sync_frame = FrameCounter;
448
449   return getAnimationFrame(graphic_info[graphic].anim_frames,
450                            graphic_info[graphic].anim_delay,
451                            graphic_info[graphic].anim_mode,
452                            graphic_info[graphic].anim_start_frame,
453                            sync_frame);
454 }
455
456 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
457                                     int graphic, int sync_frame, int mask_mode)
458 {
459   int frame = getGraphicAnimationFrame(graphic, sync_frame);
460
461   if (mask_mode == USE_MASKING)
462     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
463   else
464     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
465 }
466
467 inline void DrawGraphicAnimation(int x, int y, int graphic)
468 {
469   int lx = LEVELX(x), ly = LEVELY(y);
470
471   if (!IN_SCR_FIELD(x, y))
472     return;
473
474   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
475                           graphic, GfxFrame[lx][ly], NO_MASKING);
476   MarkTileDirty(x, y);
477 }
478
479 void DrawLevelGraphicAnimation(int x, int y, int graphic)
480 {
481   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
482 }
483
484 void DrawLevelElementAnimation(int x, int y, int element)
485 {
486   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       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   /* force PREVIEW font on preview level */
1593   game_status = GAME_MODE_PSEUDO_PREVIEW;
1594
1595   if (restart)
1596   {
1597     from_x = from_y = 0;
1598     scroll_direction = MV_RIGHT;
1599     label_state = 1;
1600     label_counter = 0;
1601
1602     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1603     DrawMicroLevelLabelExt(label_state);
1604
1605     /* initialize delay counters */
1606     DelayReached(&scroll_delay, 0);
1607     DelayReached(&label_delay, 0);
1608
1609     if (leveldir_current->name)
1610     {
1611       int len = strlen(leveldir_current->name);
1612       int lxpos = SX + (SXSIZE - len * getFontWidth(FONT_TEXT_1)) / 2;
1613       int lypos = SY + 352;
1614
1615       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
1616     }
1617
1618     game_status = last_game_status;     /* restore current game status */
1619
1620     return;
1621   }
1622
1623   /* scroll micro level, if needed */
1624   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1625       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1626   {
1627     switch (scroll_direction)
1628     {
1629       case MV_LEFT:
1630         if (from_x > 0)
1631           from_x--;
1632         else
1633           scroll_direction = MV_UP;
1634         break;
1635
1636       case MV_RIGHT:
1637         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1638           from_x++;
1639         else
1640           scroll_direction = MV_DOWN;
1641         break;
1642
1643       case MV_UP:
1644         if (from_y > 0)
1645           from_y--;
1646         else
1647           scroll_direction = MV_RIGHT;
1648         break;
1649
1650       case MV_DOWN:
1651         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1652           from_y++;
1653         else
1654           scroll_direction = MV_LEFT;
1655         break;
1656
1657       default:
1658         break;
1659     }
1660
1661     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1662   }
1663
1664   /* redraw micro level label, if needed */
1665   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
1666       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
1667       strcmp(level.author, leveldir_current->name) != 0 &&
1668       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1669   {
1670     int max_label_counter = 23;
1671
1672     if (leveldir_current->imported_from != NULL)
1673       max_label_counter += 14;
1674
1675     label_counter = (label_counter + 1) % max_label_counter;
1676     label_state = (label_counter >= 0 && label_counter <= 7 ?
1677                    MICROLABEL_LEVEL_NAME :
1678                    label_counter >= 9 && label_counter <= 12 ?
1679                    MICROLABEL_CREATED_BY :
1680                    label_counter >= 14 && label_counter <= 21 ?
1681                    MICROLABEL_LEVEL_AUTHOR :
1682                    label_counter >= 23 && label_counter <= 26 ?
1683                    MICROLABEL_IMPORTED_FROM :
1684                    label_counter >= 28 && label_counter <= 35 ?
1685                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
1686     DrawMicroLevelLabelExt(label_state);
1687   }
1688
1689   game_status = last_game_status;       /* restore current game status */
1690 }
1691
1692 int REQ_in_range(int x, int y)
1693 {
1694   if (y > DY+249 && y < DY+278)
1695   {
1696     if (x > DX+1 && x < DX+48)
1697       return 1;
1698     else if (x > DX+51 && x < DX+98) 
1699       return 2;
1700   }
1701   return 0;
1702 }
1703
1704 #define MAX_REQUEST_LINES               13
1705 #define MAX_REQUEST_LINE_LEN            7
1706
1707 boolean Request(char *text, unsigned int req_state)
1708 {
1709   int mx, my, ty, result = -1;
1710   unsigned int old_door_state;
1711   int last_game_status = game_status;   /* save current game status */
1712
1713 #if defined(PLATFORM_UNIX)
1714   /* pause network game while waiting for request to answer */
1715   if (options.network &&
1716       game_status == GAME_MODE_PLAYING &&
1717       req_state & REQUEST_WAIT_FOR)
1718     SendToServer_PausePlaying();
1719 #endif
1720
1721   old_door_state = GetDoorState();
1722
1723   UnmapAllGadgets();
1724
1725   CloseDoor(DOOR_CLOSE_1);
1726
1727   /* save old door content */
1728   BlitBitmap(bitmap_db_door, bitmap_db_door,
1729              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
1730              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
1731
1732   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1733
1734   /* clear door drawing field */
1735   DrawBackground(DX, DY, DXSIZE, DYSIZE);
1736
1737   /* force DOOR font on preview level */
1738   game_status = GAME_MODE_PSEUDO_DOOR;
1739
1740   /* write text for request */
1741   for(ty=0; ty < MAX_REQUEST_LINES; ty++)
1742   {
1743     char text_line[MAX_REQUEST_LINE_LEN + 1];
1744     int tx, tl, tc;
1745
1746     if (!*text)
1747       break;
1748
1749     for(tl=0,tx=0; tx < MAX_REQUEST_LINE_LEN; tl++,tx++)
1750     {
1751       tc = *(text + tx);
1752       if (!tc || tc == ' ')
1753         break;
1754     }
1755
1756     if (!tl)
1757     { 
1758       text++; 
1759       ty--; 
1760       continue; 
1761     }
1762
1763     strncpy(text_line, text, tl);
1764     text_line[tl] = 0;
1765
1766     DrawText(DX + (DXSIZE - tl * getFontWidth(FONT_TEXT_2)) / 2,
1767              DY + 8 + ty * (getFontHeight(FONT_TEXT_2) + 2),
1768              text_line, FONT_TEXT_2);
1769
1770     text += tl + (tc == ' ' ? 1 : 0);
1771   }
1772
1773   game_status = last_game_status;       /* restore current game status */
1774
1775   if (req_state & REQ_ASK)
1776   {
1777     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
1778     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
1779   }
1780   else if (req_state & REQ_CONFIRM)
1781   {
1782     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
1783   }
1784   else if (req_state & REQ_PLAYER)
1785   {
1786     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
1787     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
1788     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
1789     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
1790   }
1791
1792   /* copy request gadgets to door backbuffer */
1793   BlitBitmap(drawto, bitmap_db_door,
1794              DX, DY, DXSIZE, DYSIZE,
1795              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1796
1797   OpenDoor(DOOR_OPEN_1);
1798
1799 #if 0
1800   ClearEventQueue();
1801 #endif
1802
1803   if (!(req_state & REQUEST_WAIT_FOR))
1804   {
1805     SetDrawBackgroundMask(REDRAW_FIELD);
1806
1807     return FALSE;
1808   }
1809
1810   if (game_status != GAME_MODE_MAIN)
1811     InitAnimation();
1812
1813   button_status = MB_RELEASED;
1814
1815   request_gadget_id = -1;
1816
1817   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
1818
1819   while(result < 0)
1820   {
1821     if (PendingEvent())
1822     {
1823       Event event;
1824
1825       NextEvent(&event);
1826
1827       switch(event.type)
1828       {
1829         case EVENT_BUTTONPRESS:
1830         case EVENT_BUTTONRELEASE:
1831         case EVENT_MOTIONNOTIFY:
1832         {
1833           if (event.type == EVENT_MOTIONNOTIFY)
1834           {
1835             if (!PointerInWindow(window))
1836               continue; /* window and pointer are on different screens */
1837
1838             if (!button_status)
1839               continue;
1840
1841             motion_status = TRUE;
1842             mx = ((MotionEvent *) &event)->x;
1843             my = ((MotionEvent *) &event)->y;
1844           }
1845           else
1846           {
1847             motion_status = FALSE;
1848             mx = ((ButtonEvent *) &event)->x;
1849             my = ((ButtonEvent *) &event)->y;
1850             if (event.type == EVENT_BUTTONPRESS)
1851               button_status = ((ButtonEvent *) &event)->button;
1852             else
1853               button_status = MB_RELEASED;
1854           }
1855
1856           /* this sets 'request_gadget_id' */
1857           HandleGadgets(mx, my, button_status);
1858
1859           switch(request_gadget_id)
1860           {
1861             case TOOL_CTRL_ID_YES:
1862               result = TRUE;
1863               break;
1864             case TOOL_CTRL_ID_NO:
1865               result = FALSE;
1866               break;
1867             case TOOL_CTRL_ID_CONFIRM:
1868               result = TRUE | FALSE;
1869               break;
1870
1871             case TOOL_CTRL_ID_PLAYER_1:
1872               result = 1;
1873               break;
1874             case TOOL_CTRL_ID_PLAYER_2:
1875               result = 2;
1876               break;
1877             case TOOL_CTRL_ID_PLAYER_3:
1878               result = 3;
1879               break;
1880             case TOOL_CTRL_ID_PLAYER_4:
1881               result = 4;
1882               break;
1883
1884             default:
1885               break;
1886           }
1887
1888           break;
1889         }
1890
1891         case EVENT_KEYPRESS:
1892           switch(GetEventKey((KeyEvent *)&event, TRUE))
1893           {
1894             case KSYM_Return:
1895               result = 1;
1896               break;
1897
1898             case KSYM_Escape:
1899               result = 0;
1900               break;
1901
1902             default:
1903               break;
1904           }
1905           if (req_state & REQ_PLAYER)
1906             result = 0;
1907           break;
1908
1909         case EVENT_KEYRELEASE:
1910           ClearPlayerAction();
1911           break;
1912
1913         default:
1914           HandleOtherEvents(&event);
1915           break;
1916       }
1917     }
1918     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
1919     {
1920       int joy = AnyJoystick();
1921
1922       if (joy & JOY_BUTTON_1)
1923         result = 1;
1924       else if (joy & JOY_BUTTON_2)
1925         result = 0;
1926     }
1927
1928     DoAnimation();
1929
1930     /* don't eat all CPU time */
1931     Delay(10);
1932   }
1933
1934   if (game_status != GAME_MODE_MAIN)
1935     StopAnimation();
1936
1937   UnmapToolButtons();
1938
1939   if (!(req_state & REQ_STAY_OPEN))
1940   {
1941     CloseDoor(DOOR_CLOSE_1);
1942
1943     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
1944     {
1945       BlitBitmap(bitmap_db_door, bitmap_db_door,
1946                  DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
1947                  DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
1948       OpenDoor(DOOR_OPEN_1);
1949     }
1950   }
1951
1952   RemapAllGadgets();
1953
1954   SetDrawBackgroundMask(REDRAW_FIELD);
1955
1956 #if defined(PLATFORM_UNIX)
1957   /* continue network game after request */
1958   if (options.network &&
1959       game_status == GAME_MODE_PLAYING &&
1960       req_state & REQUEST_WAIT_FOR)
1961     SendToServer_ContinuePlaying();
1962 #endif
1963
1964   return result;
1965 }
1966
1967 unsigned int OpenDoor(unsigned int door_state)
1968 {
1969   unsigned int new_door_state;
1970
1971   if (door_state & DOOR_COPY_BACK)
1972   {
1973     BlitBitmap(bitmap_db_door, bitmap_db_door,
1974                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
1975                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1976     door_state &= ~DOOR_COPY_BACK;
1977   }
1978
1979   new_door_state = MoveDoor(door_state);
1980
1981   return(new_door_state);
1982 }
1983
1984 unsigned int CloseDoor(unsigned int door_state)
1985 {
1986   unsigned int new_door_state;
1987
1988   BlitBitmap(backbuffer, bitmap_db_door,
1989              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1990   BlitBitmap(backbuffer, bitmap_db_door,
1991              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
1992
1993   new_door_state = MoveDoor(door_state);
1994
1995   return(new_door_state);
1996 }
1997
1998 unsigned int GetDoorState()
1999 {
2000   return MoveDoor(DOOR_GET_STATE);
2001 }
2002
2003 unsigned int SetDoorState(unsigned int door_state)
2004 {
2005   return MoveDoor(door_state | DOOR_SET_STATE);
2006 }
2007
2008 unsigned int MoveDoor(unsigned int door_state)
2009 {
2010   static int door1 = DOOR_OPEN_1;
2011   static int door2 = DOOR_CLOSE_2;
2012   static unsigned long door_delay = 0;
2013   int x, start, stepsize = global.door_step_offset;
2014   unsigned long door_delay_value = global.door_step_delay;
2015
2016   if (door_state == DOOR_GET_STATE)
2017     return(door1 | door2);
2018
2019   if (door_state & DOOR_SET_STATE)
2020   {
2021     if (door_state & DOOR_ACTION_1)
2022       door1 = door_state & DOOR_ACTION_1;
2023     if (door_state & DOOR_ACTION_2)
2024       door2 = door_state & DOOR_ACTION_2;
2025
2026     return(door1 | door2);
2027   }
2028
2029   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2030     door_state &= ~DOOR_OPEN_1;
2031   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2032     door_state &= ~DOOR_CLOSE_1;
2033   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2034     door_state &= ~DOOR_OPEN_2;
2035   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2036     door_state &= ~DOOR_CLOSE_2;
2037
2038   if (setup.quick_doors)
2039   {
2040     stepsize = 20;
2041     door_delay_value = 0;
2042
2043     StopSound(SND_DOOR_OPENING);
2044     StopSound(SND_DOOR_CLOSING);
2045   }
2046
2047   if (global.autoplay_leveldir)
2048   {
2049     door_state |= DOOR_NO_DELAY;
2050     door_state &= ~DOOR_CLOSE_ALL;
2051   }
2052
2053   if (door_state & DOOR_ACTION)
2054   {
2055     if (!(door_state & DOOR_NO_DELAY))
2056     {
2057       /* opening door sound has priority over simultaneously closing door */
2058       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2059         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2060       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2061         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2062     }
2063
2064     start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2065
2066     for(x=start; x<=DXSIZE; x+=stepsize)
2067     {
2068       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2069       GC gc = bitmap->stored_clip_gc;
2070
2071       if (!(door_state & DOOR_NO_DELAY))
2072         WaitUntilDelayReached(&door_delay, door_delay_value);
2073
2074       if (door_state & DOOR_ACTION_1)
2075       {
2076         int i = (door_state & DOOR_OPEN_1 ? DXSIZE-x : x);
2077         int j = (DXSIZE - i) / 3;
2078
2079         BlitBitmap(bitmap_db_door, drawto,
2080                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i/2,
2081                    DXSIZE,DYSIZE - i/2, DX, DY);
2082
2083         ClearRectangle(drawto, DX, DY + DYSIZE - i/2, DXSIZE,i/2);
2084
2085         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2086         BlitBitmapMasked(bitmap, drawto,
2087                          DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2088                          DX + DXSIZE - i, DY + j);
2089         BlitBitmapMasked(bitmap, drawto,
2090                          DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2091                          DX + DXSIZE - i, DY + 140 + j);
2092         SetClipOrigin(bitmap, gc, DX - DXSIZE + i, DY - (DOOR_GFX_PAGEY1 + j));
2093         BlitBitmapMasked(bitmap, drawto,
2094                          DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2095                          DX, DY);
2096         BlitBitmapMasked(bitmap, drawto,
2097                          DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2098                          DX, DY + 140 - j);
2099
2100         BlitBitmapMasked(bitmap, drawto,
2101                          DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2102                          DX, DY + 77 - j);
2103         BlitBitmapMasked(bitmap, drawto,
2104                          DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2105                          DX, DY + 203 - j);
2106         SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2107         BlitBitmapMasked(bitmap, drawto,
2108                          DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2109                          DX + DXSIZE - i, DY + 77 + j);
2110         BlitBitmapMasked(bitmap, drawto,
2111                          DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2112                          DX + DXSIZE - i, DY + 203 + j);
2113
2114         redraw_mask |= REDRAW_DOOR_1;
2115       }
2116
2117       if (door_state & DOOR_ACTION_2)
2118       {
2119         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - x : x);
2120         int j = (VXSIZE - i) / 3;
2121
2122         BlitBitmap(bitmap_db_door, drawto,
2123                    DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i/2,
2124                    VXSIZE, VYSIZE - i/2, VX, VY);
2125
2126         ClearRectangle(drawto, VX, VY + VYSIZE-i/2, VXSIZE, i/2);
2127
2128         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2129         BlitBitmapMasked(bitmap, drawto,
2130                          VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2131                          VX + VXSIZE-i, VY+j);
2132         SetClipOrigin(bitmap, gc,
2133                       VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2134         BlitBitmapMasked(bitmap, drawto,
2135                          VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2136                          VX, VY);
2137
2138         BlitBitmapMasked(bitmap, drawto,
2139                          VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2140                          i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2141         SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2142         BlitBitmapMasked(bitmap, drawto,
2143                          VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2144                          i, VYSIZE / 2 - j,
2145                          VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2146
2147         redraw_mask |= REDRAW_DOOR_2;
2148       }
2149
2150       BackToFront();
2151
2152       if (game_status == GAME_MODE_MAIN)
2153         DoAnimation();
2154     }
2155   }
2156
2157   if (setup.quick_doors)
2158   {
2159     StopSound(SND_DOOR_OPENING);
2160     StopSound(SND_DOOR_CLOSING);
2161   }
2162
2163   if (door_state & DOOR_ACTION_1)
2164     door1 = door_state & DOOR_ACTION_1;
2165   if (door_state & DOOR_ACTION_2)
2166     door2 = door_state & DOOR_ACTION_2;
2167
2168   return (door1 | door2);
2169 }
2170
2171 void DrawSpecialEditorDoor()
2172 {
2173   /* draw bigger toolbox window */
2174   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2175              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2176              EX - 4, EY - 12);
2177   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2178              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2179              EX - 4, EY - 4);
2180
2181   redraw_mask |= REDRAW_ALL;
2182 }
2183
2184 void UndrawSpecialEditorDoor()
2185 {
2186   /* draw normal tape recorder window */
2187   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2188              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2189              EX - 4, EY - 12);
2190
2191   redraw_mask |= REDRAW_ALL;
2192 }
2193
2194
2195 /* ---------- new tool button stuff ---------------------------------------- */
2196
2197 /* graphic position values for tool buttons */
2198 #define TOOL_BUTTON_YES_XPOS            2
2199 #define TOOL_BUTTON_YES_YPOS            250
2200 #define TOOL_BUTTON_YES_GFX_YPOS        0
2201 #define TOOL_BUTTON_YES_XSIZE           46
2202 #define TOOL_BUTTON_YES_YSIZE           28
2203 #define TOOL_BUTTON_NO_XPOS             52
2204 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2205 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2206 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2207 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2208 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2209 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2210 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2211 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2212 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2213 #define TOOL_BUTTON_PLAYER_XSIZE        30
2214 #define TOOL_BUTTON_PLAYER_YSIZE        30
2215 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2216 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2217 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2218 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2219 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2220                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2221 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2222                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2223 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2224                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2225 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2226                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2227 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2228                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2229 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2230                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2231 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2232                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2233 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2234                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2235
2236 static struct
2237 {
2238   int xpos, ypos;
2239   int x, y;
2240   int width, height;
2241   int gadget_id;
2242   char *infotext;
2243 } toolbutton_info[NUM_TOOL_BUTTONS] =
2244 {
2245   {
2246     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2247     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2248     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2249     TOOL_CTRL_ID_YES,
2250     "yes"
2251   },
2252   {
2253     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2254     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2255     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2256     TOOL_CTRL_ID_NO,
2257     "no"
2258   },
2259   {
2260     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2261     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2262     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2263     TOOL_CTRL_ID_CONFIRM,
2264     "confirm"
2265   },
2266   {
2267     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2268     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2269     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2270     TOOL_CTRL_ID_PLAYER_1,
2271     "player 1"
2272   },
2273   {
2274     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2275     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2276     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2277     TOOL_CTRL_ID_PLAYER_2,
2278     "player 2"
2279   },
2280   {
2281     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2282     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2283     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2284     TOOL_CTRL_ID_PLAYER_3,
2285     "player 3"
2286   },
2287   {
2288     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2289     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2290     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2291     TOOL_CTRL_ID_PLAYER_4,
2292     "player 4"
2293   }
2294 };
2295
2296 void CreateToolButtons()
2297 {
2298   int i;
2299
2300   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2301   {
2302     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2303     Bitmap *deco_bitmap = None;
2304     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2305     struct GadgetInfo *gi;
2306     unsigned long event_mask;
2307     int gd_xoffset, gd_yoffset;
2308     int gd_x1, gd_x2, gd_y;
2309     int id = i;
2310
2311     event_mask = GD_EVENT_RELEASED;
2312
2313     gd_xoffset = toolbutton_info[i].xpos;
2314     gd_yoffset = toolbutton_info[i].ypos;
2315     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2316     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2317     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2318
2319     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2320     {
2321       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2322
2323       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2324                            &deco_bitmap, &deco_x, &deco_y);
2325       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2326       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2327     }
2328
2329     gi = CreateGadget(GDI_CUSTOM_ID, id,
2330                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2331                       GDI_X, DX + toolbutton_info[i].x,
2332                       GDI_Y, DY + toolbutton_info[i].y,
2333                       GDI_WIDTH, toolbutton_info[i].width,
2334                       GDI_HEIGHT, toolbutton_info[i].height,
2335                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2336                       GDI_STATE, GD_BUTTON_UNPRESSED,
2337                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2338                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2339                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2340                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2341                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2342                       GDI_DECORATION_SHIFTING, 1, 1,
2343                       GDI_EVENT_MASK, event_mask,
2344                       GDI_CALLBACK_ACTION, HandleToolButtons,
2345                       GDI_END);
2346
2347     if (gi == NULL)
2348       Error(ERR_EXIT, "cannot create gadget");
2349
2350     tool_gadget[id] = gi;
2351   }
2352 }
2353
2354 void FreeToolButtons()
2355 {
2356   int i;
2357
2358   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2359     FreeGadget(tool_gadget[i]);
2360 }
2361
2362 static void UnmapToolButtons()
2363 {
2364   int i;
2365
2366   for (i=0; i<NUM_TOOL_BUTTONS; i++)
2367     UnmapGadget(tool_gadget[i]);
2368 }
2369
2370 static void HandleToolButtons(struct GadgetInfo *gi)
2371 {
2372   request_gadget_id = gi->custom_id;
2373 }
2374
2375 int get_next_element(int element)
2376 {
2377   switch(element)
2378   {
2379     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2380     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2381     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2382     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2383     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2384     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2385     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2386
2387     default:                            return element;
2388   }
2389 }
2390
2391 int el_act_dir2img(int element, int action, int direction)
2392 {
2393   direction = MV_DIR_BIT(direction);
2394
2395   return element_info[element].direction_graphic[action][direction];
2396 }
2397
2398 int el_act2img(int element, int action)
2399 {
2400   return element_info[element].graphic[action];
2401 }
2402
2403 int el_dir2img(int element, int direction)
2404 {
2405   return el_act_dir2img(element, ACTION_DEFAULT, direction);
2406 }
2407
2408 int el2img(int element)
2409 {
2410   return element_info[element].graphic[ACTION_DEFAULT];
2411 }
2412
2413 int el2edimg(int element)
2414 {
2415   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
2416 }
2417
2418 int el2preimg(int element)
2419 {
2420   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
2421 }