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