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