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