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