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