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